# -*- coding: utf-8 -*-
# @file    : config
# @Date    : 2020/11/20
# @Author  :
# @Version : 1.0.0
import re
import configparser

from . import _covert_func
from ._types import configdict, sectiondict
from .constant import CONFIG_PATH


class ConfigParser(configparser.ConfigParser):
    """
    config parser 保留大小写

    """

    def optionxform(self, optionstr):
        return optionstr


class Config(object):
    """
    配置加载器

    """

    def __init__(self, file_path, encoding='utf-8'):
        """

        :param file_path: 配置文件的路径
        :param encoding: 文件的编码格式
        """
        self.__config_parser = ConfigParser()
        self.__config_parser.read(file_path, encoding=encoding)
        self.__options = {}  # 注册的选项
        self.__data = {}  # 配置取值存取的地方
        self.__load_count = 0  # 加载配置的数量

        self.init()
        self.load()

    def init(self):
        """
        初始化操作/注册需要加载的配置选项
        :return:
        """
        # flask  options
        self.add_option(Option('flask', '^.*$', bool, None))

        # user options
        self.add_option(Option('user', '^host$', str, None))
        self.add_option(Option('user', '^web_port$', int, None))
        self.add_option(Option('user', '^live_search_port$', int, None))
        self.add_option(Option('user', '^static_dir', str, None))
        self.add_option(Option('user', '^cookie_file$', str, None))
        self.add_option(Option('user', '^api_cache_list', str, _covert_func.api_cache_list))
        self.add_option(Option('user', '^proxy_time_out', float, None))

    def add_option(self, option):
        """
        注册配置选项
        :param option:
        :return:
        """
        if option.section not in self.__options:
            self.__options[option.section] = []
        self.__options[option.section].append(option)

    def load(self):
        """
        加载配置并转换取值后存入`__data`中
        :return:
        """
        self.__data = {}
        for s in self.__config_parser.sections():
            if s not in self.__options:
                continue
            for k, v in self.__config_parser[s].items():
                for op in self.__options[s]:
                    if not op.match(k):
                        continue
                    self.__load(s, k, op.convert(v))
        self.__freeze_data()

    def __load(self, section, option, value):
        """
        辅助load
        :param section:
        :param option:
        :param value:
        :return:
        """
        if section not in self.__data:
            self.__data[section] = {}
        self.__data[section][option] = value
        self.__load_count += 1

    def __freeze_data(self):
        """
        冻结加载配置， 防止被修改
        :return:
        """
        frozen_data = {}
        for section, options in self.__data.items():
            frozen_data[section] = sectiondict(options)
        frozen_data = configdict(frozen_data)
        self.__data = frozen_data

    def __getitem__(self, item):
        """
        config['section'] -> 取出section字典
        config['section']['option'] -> 取出选项取值， 找不到时返回None
        :param item:
        :return:
        """
        return self.__data[item]

    def __repr__(self):
        return "<Config load_count=%s>" % self.__load_count


class Option(object):
    """
    选项

    """

    def __init__(self, section, option_re, _type, convert_func=None, ignore_case=False):
        """

        :param section: 位于的小节名
        :param option_re: 选项的正则表达式
        :param _type: 选项的取值类型
        :param convert_func: 转换函数
        :param ignore_case: 是否忽略大小写
        """
        self.section = section
        self.option_re = option_re
        self.type = _type
        self.convert_func = convert_func
        self.ignore_case = ignore_case

    def match(self, raw):
        """
        正则匹配选项名称
        :param raw:
        :return:
        """
        flags = (self.ignore_case or 0) and re.IGNORECASE
        return re.match(self.option_re, raw, flags=flags)

    def convert(self, raw):
        """
        对原始值进行格式转换
        :param raw:
        :return:
        """
        value = self.type(raw)
        if self.convert_func:
            value = self.convert_func(value)
        return value

    def __repr__(self):
        return "<Option %s/%s>" % (self.section, self.option_re)


config = Config(CONFIG_PATH)
